package com.example.activititest.service.impl;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.activititest.config.CustomProcessDiagramGenerator;
import com.example.activititest.service.ActivitiTaskService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service("activitiTaskService")
public class ActivitiTaskServiceImpl implements ActivitiTaskService {
	private static final Logger log = LoggerFactory.getLogger(ActivitiTaskServiceImpl.class);
	@Autowired
	private TaskService taskService;

	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private HistoryService historyService;

	@Autowired
	private ProcessEngine processEngine;

	@Autowired
	private RuntimeService runtimeService;

	@Override
	public List<Task> querySignPage(Map<String, Object> params) {
		int page = Integer.parseInt((String) params.get("page"));
		int pageSize = Integer.parseInt((String) params.get("pageSize"));
		// 当前用户assignee获得对应的角色组，此处为了方便测试从前台传入
		TaskQuery taskQuery = taskService.createTaskQuery().taskUnassigned();
		if (params.get("group") != null) {
			taskQuery = taskQuery.taskCandidateGroup(params.get("group").toString());
		}
		if (params.get("assigned") != null) {
			taskQuery.taskCandidateOrAssigned(params.get("assigned").toString());
		}
		if (params.get("key") != null) {
			taskQuery.processDefinitionKey(params.get("key").toString());
		}
		if (params.get("category") != null) {
			List<String> categorys = new ArrayList<>();
			categorys.add(params.get("category").toString());
			taskQuery.processCategoryIn(categorys);
		}
		List<Task> list = taskQuery.orderByTaskCreateTime().desc().listPage((page - 1) * pageSize, pageSize);
		return list;
	}

	@Override
	public List<HistoricProcessInstance> querySentPage(Map<String, Object> params) {
		int page = Integer.parseInt((String) params.get("page"));
		int pageSize = Integer.parseInt((String) params.get("pageSize"));
		// 当前用户assignee，此处为了方便测试从前台传入
		HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
		if (params.get("startedBy") != null) {
			historicProcessInstanceQuery.startedBy(params.get("startedBy").toString());
		}
		if (params.get("key") != null) {
			historicProcessInstanceQuery.processDefinitionKey(params.get("key").toString());
		}
		if (params.get("category") != null) {
			historicProcessInstanceQuery.processDefinitionCategory(params.get("category").toString());
		}
		List<HistoricProcessInstance> list = historicProcessInstanceQuery.listPage((page - 1) * pageSize, pageSize);
		return list;
	}

	@Override
	public List<HistoricProcessInstance> queryFinishPage(Map<String, Object> params) {
		int page = Integer.parseInt((String) params.get("page"));
		int pageSize = Integer.parseInt((String) params.get("pageSize"));
		// 当前用户assignee，此处为了方便测试从前台传入
		HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
		if (params.get("assigned") != null) {
			historicProcessInstanceQuery = historicProcessInstanceQuery.involvedUser(params.get("assigned").toString());
		}
		if (params.get("key") != null) {
			historicProcessInstanceQuery.processDefinitionKey(params.get("key").toString());
		}
		if (params.get("category") != null) {
			historicProcessInstanceQuery.processDefinitionCategory(params.get("category").toString());
		}
		List<HistoricProcessInstance> list = historicProcessInstanceQuery.finished().listPage((page - 1) * pageSize,
				pageSize);
		return list;
	}

	@Override
	public void claim(String id, String assignee) {
		// 当前用户assignee，此处为了方便测试从前台传入
		taskService.claim(id, assignee);
	}

	@Override
	public List queryTodoPage(Map<String, Object> params) {
		int page = Integer.parseInt((String) params.get("page"));
		int pageSize = Integer.parseInt((String) params.get("pageSize"));
		// 当前用户assignee，此处为了方便测试从前台传入 获得待办任务以及签收的任务
		TaskQuery taskQuery = taskService.createTaskQuery();
		if (params.get("assignee") != null) {
			taskQuery.taskCandidateOrAssigned(params.get("assignee").toString());
		}
		if (params.get("category") != null) {
			List<String> categorys = new ArrayList<>();
			categorys.add(params.get("category").toString());
			taskQuery.processCategoryIn(categorys);
		}
		if (params.get("key") != null) {
			taskQuery.processDefinitionKey(params.get("key").toString());
		}
		List<Task> list = taskQuery.orderByTaskCreateTime().desc().listPage((page - 1) * pageSize, pageSize);
		return list;
	}

	@Override
	public void complete(String taskId, String comment, Map<String, Object> variables) {
		// 批注信息
		if (StringUtils.isNotBlank(comment)) {
			// 1.根据任务ID查询任务实例
			Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
			// 2.从任务里面取出流程实例ID
			String processInstanceId = task.getProcessInstanceId();
			// 3.设置批注人
			if (variables.get("assignee") != null) {
				Authentication.setAuthenticatedUserId(variables.get("assignee").toString());
			} else {
				Authentication.setAuthenticatedUserId(task.getAssignee());
			}
			taskService.addComment(taskId, processInstanceId, String.valueOf(comment));
		}
		taskService.complete(taskId, variables);
	}

	@Override
	public List queryCommentPage(String taskId) {
		// 1,根据任务ID查询任务实例
		Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
		// 2,从任务里面取出流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
		return list;
	}

	/**
	 * 通过任务id 获取流程图像，已执行节点和流程线高亮显示
	 * @throws Exception 
	 */
	@Override
	public void getActivitiProccessImage(String processInstanceId, HttpServletResponse response) throws Exception {

		InputStream imageStream = null;
		BpmnModel bpmnModel = null;
		List<String> highLightedActivitiIdList = new ArrayList<>();
		List<String> runningActivitiIdList = new ArrayList<>();
		List<String> highLightedFlowIds = new ArrayList<>();
		// 通过流程实例ID获取历史流程实例
		HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		if (null != historicProcessInstance) {
			// 获取流程定义Model对象
			bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
			// 如果需要展示历史节点记录，则需要查询出对应的历史节点信息
			// 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
			List<HistoricActivityInstance> historicActivityInstanceList = historyService
					.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
					.orderByHistoricActivityInstanceId().asc().list();
			// 将已经执行的节点ID放入高亮显示节点集合
			for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
				highLightedActivitiIdList.add(historicActivityInstance.getActivityId());
			}
			// 通过流程实例ID获取流程中正在执行的节点
			List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery()
					.processInstanceId(processInstanceId).list();
			for (Execution execution : runningActivityInstanceList) {
				if (StringUtils.isNotEmpty(execution.getActivityId())) {
					runningActivitiIdList.add(execution.getActivityId());
				}
			}
			// 获取已流经的流程线，需要高亮显示高亮流程已发生流转的线id集合
			highLightedFlowIds.addAll(getHighLightedFlows(bpmnModel, historicActivityInstanceList));
		}
		try {
			if (null != bpmnModel) {
				// 定义流程画布生成器
				CustomProcessDiagramGenerator processDiagramGenerator = new CustomProcessDiagramGenerator();
				// 使用默认配置获得流程图表生成器，并生成追踪图片字符流
				imageStream = processDiagramGenerator.generateDiagramCustom(bpmnModel, "png", highLightedActivitiIdList,
						runningActivitiIdList, highLightedFlowIds, "宋体", "黑体", "黑体", null, 2.0);
				response.setContentType("image/png");
				OutputStream os = response.getOutputStream();
				int bytesRead = 0;
				byte[] buffer = new byte[8192];
				while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
					os.write(buffer, 0, bytesRead);
				}
				os.close();
				imageStream.close();
				log.info("[完成]-获取流程图图像");
			}
		} catch (Exception e) {
			log.error("通过流程实例ID[{}]获取流程图时出现异常！", processInstanceId, e);
			throw new Exception("通过流程实例ID" + processInstanceId + "获取流程图时出现异常！", e);
		}
	}

	/**
	 * 获取已流经的流程线，需要高亮显示高亮流程已发生流转的线id集合
	 *
	 * @param bpmnModel
	 * @param historicActivityInstanceList
	 * @return
	 */
	private List<String> getHighLightedFlows(BpmnModel bpmnModel,
			List<HistoricActivityInstance> historicActivityInstanceList) {
		// 已流经的流程线，需要高亮显示
		List<String> highLightedFlowIdList = new ArrayList<>();
		// 全部活动节点
		List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();
		// 已完成的历史活动节点
		List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();
		for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
			// 获取流程节点
			FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess()
					.getFlowElement(historicActivityInstance.getActivityId(), true);
			allHistoricActivityNodeList.add(flowNode);
			// 结束时间不为空，当前节点则已经完成
			if (historicActivityInstance.getEndTime() != null) {
				finishedActivityInstanceList.add(historicActivityInstance);
			}
		}
		FlowNode currentFlowNode;
		FlowNode targetFlowNode;
		HistoricActivityInstance currentActivityInstance;
		// 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
		for (int k = 0; k < finishedActivityInstanceList.size(); k++) {
			currentActivityInstance = finishedActivityInstanceList.get(k);
			// 获得当前活动对应的节点信息及outgoingFlows信息
			currentFlowNode = (FlowNode) bpmnModel.getMainProcess()
					.getFlowElement(currentActivityInstance.getActivityId(), true);
			// 当前节点的所有流出线
			List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();

			/**
			 * 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转：
			 * 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
			 * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
			 * (第2点有问题，有过驳回的，会只绘制驳回的流程线，通过走向下一级的流程线没有高亮显示)
			 */
			if ("parallelGateway".equals(currentActivityInstance.getActivityType())
					|| "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
				// 遍历历史活动节点，找到匹配流程目标节点的
				for (SequenceFlow outgoingFlow : outgoingFlowList) {
					// 获取当前节点流程线对应的下级节点
					targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(),
							true);
					// 如果下级节点包含在所有历史节点中，则将当前节点的流出线高亮显示
					if (allHistoricActivityNodeList.contains(targetFlowNode)) {
						highLightedFlowIdList.add(outgoingFlow.getId());
					}
				}
			} else {
				/**
				 * 2、当前节点不是并行网关或兼容网关 【已解决-问题】如果当前节点有驳回功能，驳回到申请节点，
				 * 则因为申请节点在历史节点中，导致当前节点驳回到申请节点的流程线被高亮显示，但实际并没有进行驳回操作
				 */
				// 当前节点ID
				String currentActivityId = currentActivityInstance.getActivityId();
				int size = historicActivityInstanceList.size();
				boolean ifStartFind = false;
				boolean ifFinded = false;
				HistoricActivityInstance historicActivityInstance;
				// 循环当前节点的所有流出线
				// 循环所有历史节点
				// log.info("【开始】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
				// log.info("循环历史节点");
				for (int i = 0; i < historicActivityInstanceList.size(); i++) {
					// //
					// 如果当前节点流程线对应的下级节点在历史节点中，则该条流程线进行高亮显示（【问题】有驳回流程线时，即使没有进行驳回操作，因为申请节点在历史节点中，也会将驳回流程线高亮显示-_-||）
					// if
					// (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef()))
					// {
					// Map<String, Object> map = new HashMap<>();
					// map.put("highLightedFlowId", sequenceFlow.getId());
					// map.put("highLightedFlowStartTime",
					// historicActivityInstance.getStartTime().getTime());
					// tempMapList.add(map);
					// // highLightedFlowIdList.add(sequenceFlow.getId());
					// }
					// 历史节点
					historicActivityInstance = historicActivityInstanceList.get(i);
					// log.info("第【{}/{}】个历史节点-ActivityId=[{}]", i + 1, size,
					// historicActivityInstance.getActivityId());
					// 如果循环历史节点中的id等于当前节点id，从当前历史节点继续先后查找是否有当前节点流程线等于的节点
					// 历史节点的序号需要大于等于已完成历史节点的序号，防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示，第二次的不显示
					if (i >= k && historicActivityInstance.getActivityId().equals(currentActivityId)) {
						// log.info("第[{}]个历史节点和当前节点一致-ActivityId=[{}]", i + 1,
						// historicActivityInstance.getActivityId());
						ifStartFind = true;
						// 跳过当前节点继续查找下一个节点
						continue;
					}
					if (ifStartFind) {
						// log.info("[开始]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);

						ifFinded = false;
						for (SequenceFlow sequenceFlow : outgoingFlowList) {
							// 如果当前节点流程线对应的下级节点在其后面的历史节点中，则该条流程线进行高亮显示
							// 【问题】
							// log.info("当前流出线的下级节点=[{}]", sequenceFlow.getTargetRef());
							if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
								// log.info("当前节点[{}]需高亮显示的流出线=[{}]", currentActivityId, sequenceFlow.getId());
								highLightedFlowIdList.add(sequenceFlow.getId());
								// 暂时默认找到离当前节点最近的下一级节点即退出循环，否则有多条流出线时将全部被高亮显示
								ifFinded = true;
								break;
							}
						}
						// log.info("[完成]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
					}
					if (ifFinded) {
						// 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环，否则有多条流出线时将全部被高亮显示
						break;
					}
				}
				// log.info("【完成】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
			}

		}
		return highLightedFlowIdList;
	}
}
